import { Res } from '../../Common/UIResources';
import GameMgr from '../../Logic/GameMgr';
import { INVALID_VALUE, OBJECT_COPY } from './../../../Core/CoreDefine';
import { ObjectWrap } from './../../../Core/FrameEx/ES5Ex';
import { AudioMgr } from './../../../Core/Manager/AudioMgr';
import { JXBattleCb } from './JXBattleCb';
import { JXBattleEvt, JXBtlArrIx, JXBtlBEM } from './JXBattleDefine';
import { JXRBBullet } from './JXRBBullet';
import JXRBCmdMgr from './JXRBCmdMgr';
import { JXRBCollisionEffect } from './JXRBCollisionEffect';
import { JXRBRole } from "./JXRBRole";
import { COMMON_ANI_NAME, DescribeType, JXBattleEffectsRoot, JXBattleSkillSpine, JXCLActionConfigure, JXEAniEventName, JXEAniNames, JXECollisionEffectFlag, JXECollsionType, JXEDir, JXEScriptID, JXETargetPosType, JXRB_ROLE_LAYER_NAME } from './JXULDefine';


const MOVE_TIME = 0.1;

interface IEffectEvent {
	hurtMul: number,
	cureMul: number,
	isEffect: boolean,
	isBullet: boolean,
	eventName: string
}


/** 动作结束标志，只有标志被清零的时候才表示所有的条件已完成 */
const END_FLAG = {
	/** 动画标志 */
	ANIMATION: 1 << 1,
	/** 动作标志 */
	ACTION: 1 << 2,
	/** 子弹标志 */
	BULLET: 1 << 3,
}
/**
 * 动作脚本
 */
export class JXRBScript extends ObjectWrap {
	/** 执行者 */
	public execer: JXRBRole = null;
	/** 管理器 */
	protected _cmd: JXRBCmdMgr = null;
	/** 目标 */
	public targets: JXRBRole[] = null;
	/** 动作配置 */
	public actionRaw: SActionRaw;
	/** 动作名称 */
	public _aniConfig: JXCLActionConfigure;
	/** 子弹集合 */
	public _bullets: JXRBBullet[];
	/** 结束标志 */
	public _endFlag: number = 0;

	protected _recordId: number;

	/** 当前段数, 提供给分段动作使用 */
	protected _curSegment: number;
	/** 分段总数 */
	protected _segmentCount: number;

	/** 退出执行的动作 */
	public existAcions: cc.Action[] = [];
	/** 表现分段逻辑： 这个分段和前面的分段不是一回事，这个分段是基于表现上的分段，上面的分段是基于技能产生效果的分段 
	 * 为0的时候出现Effects则标识不需要分段。
	 * 该字段标识已经分段过后的系数总和
	 * 只有伤害和治疗会进行分段
	*/
	protected _ansSegmentInfos: { [id: string]: number } = {};
	/** 是否已经发射子弹 */
	public isShoots: boolean;

	public get ID(): JXEScriptID {
		return null;
	}

	public get collsionType(): JXECollsionType {
		return null;
	}

	constructor(actionRaw: SActionRaw) {
		super();
		this.actionRaw = actionRaw;
	}

	/** 绑定执行者 */
	public bindExecer(role: JXRBRole) {
		this.execer = role;
		return this;
	}

	/** 绑定管理器 */
	public bindCmd(cmd: JXRBCmdMgr) {
		this._cmd = cmd;
		return this;
	}

	public bindActionRaw(action: SActionRaw) {
		this.actionRaw = action;
		return this;
	}


	public bindAniConfigure(aniConfig: JXCLActionConfigure) {
		this._aniConfig = aniConfig;
		this.targets = [];
		if (this._aniConfig.effectSk) {
			let path = JXBattleSkillSpine + this._aniConfig.effectSk;
			this.execer.setEffectSkeletonData(this._cmd.assetManager.assetImpl.getPreLoadAsset<sp.SkeletonData>(path));
		}
		if (this._aniConfig.isSkill && this.collsionType == JXECollsionType.Siglon) {
			let skillRaw = JXBattleCb.getSkillData(this.skillId());
			this._segmentCount = skillRaw.skillEffect.length;
			let ansSkill = this._aniConfig.ansInfo as JXAnsSkillInfo;
			for (let i = 0; i < ansSkill.effects.length; i++) {
				let effect = ansSkill.effects[i];
				this.targets.push(this._cmd.getRoleById(effect.target));
			}
		}
		else if (!this._aniConfig.isSkill) {
			let ansAtk = this._aniConfig.ansInfo as JXAnsHurtInfo;
			this.targets.push(this._cmd.getRoleById(ansAtk.target));
		} else if (this.collsionType == JXECollsionType.InsEffect) {
			let ansSkill = this._aniConfig.ansInfo as JXAnsSkillInfo;
			if (!ansSkill) return;
			this._segmentCount = ansSkill.effects.length;
			for (let i = 0; i < ansSkill.effects.length; i++) {
				let effect = ansSkill.effects[i];
				let role = this._cmd.getRoleById(effect.target);
				this.targets.push(role);
			}
		} else {
			let ansSkill = this._aniConfig.ansInfo as JXAnsSkillInfo;
			if (!ansSkill) return;
			for (let i = 0; i < ansSkill.effects.length; i++) {
				let effect = ansSkill.effects[i];
				let role = this._cmd.getRoleById(effect.target);
				if (this.targets.indexOf(role) != INVALID_VALUE) continue;
				this.targets.push(role);
			}
		}
		return this;
	}

	public setRecordIdx(idx: number) {
		this._recordId = idx;
	}

	public break() {
		if (this._bullets && !this.isShoots) {
			for (let i = 0; i < this._bullets.length; i++) {
				this._bullets[i].destroy();
			}
			this._bullets.length = 0;
		}

		this.execer.scrpitBreak(this)
	}

	public skillId(): number {
		if (this._aniConfig.isSkill) {
			let skillAns = this._aniConfig.ansInfo as JXAnsSkillInfo;
			return skillAns.skillId;
		}
		return null;
	}

	/** 执行 */
	public exec() {

	}

	public playAudio() {
		if (this._aniConfig.isSkill) {
			let audio = Res.audio.battle + this.skillId();
			AudioMgr.Ins().playEffect(audio);
		} else {
			let raw = GameMgr.heroData.getRaw<SHeroDataRaw>(this.execer.tableId);
			let auido = raw.atkAudio;
			AudioMgr.Ins().playEffect(Res.audio.battle + auido);
		}
	}

	/** 脚本执行完成，退出 */
	public exit() {
		this._ansSegmentInfos = null;
		let isResetPos = true;
		if (this._aniConfig.isSkill) {
			let skillAns = this._aniConfig.ansInfo as JXAnsSkillInfo;
			let curInsId = skillAns.insId;
			let nextCmd = this._cmd.getNextCmd(this.execer.id);
			let passiveCmds = [];
			let curCmds = null;
			let passiveDepth = 0;
			// 技能释放期间的其他命令处理。
			while ((nextCmd && (nextCmd[JXBtlArrIx.Zero] != JXBattleEvt.ROLE_SKILL_PRESS_END) && nextCmd[JXBtlArrIx.One] != curInsId) || passiveDepth) {
				if (nextCmd && (nextCmd[JXBtlArrIx.Zero] == JXBattleEvt.ROLE_SKILL_PRESS)) {
					passiveDepth++;
					curCmds = [];
					curCmds.push(nextCmd);
				}
				else if (nextCmd && (nextCmd[JXBtlArrIx.Zero] == JXBattleEvt.ROLE_SKILL_PRESS_END)) {
					passiveDepth--;
					curCmds.push(nextCmd);
					passiveCmds.unshift(curCmds);
					curCmds = null;
				}
				else if (curCmds) {
					curCmds.push(nextCmd);
				}
				nextCmd = this._cmd.getNextCmd(this.execer.id);
				if (nextCmd && passiveDepth == 0 && nextCmd[JXBtlArrIx.Zero] == JXBattleEvt.ROLE_REMOVE_BUFF) {
					this._cmd.removeBuff(nextCmd[JXBtlArrIx.Five], nextCmd[JXBtlArrIx.One], nextCmd[JXBtlArrIx.Two]);
				}
			}
			if (passiveCmds.length > 0) {
				this.execer.addPassiveCmds(passiveCmds);
			}
		} else {
			let hurtInfo = this._aniConfig.ansInfo as JXAnsHurtInfo;
			// 如果攻击的目标没有死亡，切存在追击技能，则不回去原地
			if (hurtInfo && hurtInfo.hasPursuit && !hurtInfo.targetIsDie) {
				isResetPos = false;
			}
		}
		this.execer.scrpitExit(this, isResetPos);
	}

	/**获取animation 名称 */
	protected getBaseAnimName(airPos?: cc.Vec2): string {
		let target = this.targets[0];
		let targetPos = airPos ? airPos : target.position;
		let dir = (targetPos.x < this.execer.x ? JXEDir.Left : JXEDir.Right);
		this.execer.setDir(dir);
		let curPos = this.execer.position;
		if (!this._aniConfig.isSkill) {
			let aniName = ""
			let baseAni = JXEAniNames.comAtk;
			baseAni = this.execer.postfix ? baseAni + "2" : baseAni;
			if (targetPos.y > curPos.y) {
				aniName = baseAni + "_up";
			} else if (targetPos.y < curPos.y) {
				aniName = baseAni + "_down";
			} else {
				aniName = baseAni;
			}
			let aniInfo = this.execer.skeleton.getAnimationInfo(aniName);
			if (!aniInfo) {
				return baseAni;
			} else {
				return aniName;
			}
		}
		else {
			let aniName = ""
			let baseAni = JXEAniNames.skill + (this._aniConfig.skillGroup ? this._aniConfig.skillGroup : "");
			baseAni = this.execer.postfix ? baseAni + "_2" : baseAni;
			if (targetPos.y > curPos.y) {
				aniName = baseAni + "_up"
			} else if (targetPos.y < curPos.y) {
				aniName = baseAni + "_down"
			} else {
				aniName = baseAni;
			}
			let aniInfo = this.execer.skeleton.getAnimationInfo(aniName);
			if (!aniInfo) {
				return baseAni;
			} else {
				return aniName;
			}
		}
	}

	protected getComFullAnimNames(): string[] {
		let aniBase = this.getBaseAnimName();
		// if (!this._aniConfig.isSkill) return [aniBase];
		// if (this._aniConfig.isPrepare) return [this.appendAnimName(aniBase, JXBtlArrIx.One)];
		// return [this.appendAnimName(aniBase, JXBtlArrIx.One)];
		return [aniBase]
	}

	protected appendAnimName(base: string, index: number): string {
		return this.execer.appendAnimName(base, index);
	}

	/**当个目标位置获取 */
	protected getSingleTargetPos(): cc.Vec2 {
		let targetPos: cc.Vec2;
		if (this.actionRaw.targetType == JXETargetPosType.Target) {
			var target = this.targets[0];
			if (target == this.execer) {
				targetPos = this.execer.position;
			}
			else {
				targetPos = target.position;
				if (this.actionRaw.airOffsetX) {
					targetPos.x += this.actionRaw.airOffsetX * target.dir;
				} else {
					targetPos.x += (target.skeleton.node["sWidth"] / 4 + this.execer.skeleton.node["sWidth"] / 4) * target.dir;
				}
			}
		}
		// else if (this.actionRaw.targetType == JXETargetPosType.SelfCamp) {
		// 	targetPos = this.execer.dir == JXEDir.Right ? JXRB_WARFIELD_IDX_MAP.RT.clone() : JXRB_WARFIELD_IDX_MAP.LT.clone();
		// }
		// else if (this.actionRaw.targetType == JXETargetPosType.TargetCamp) {
		// 	targetPos = this.targets[0].dir == JXEDir.Right ? JXRB_WARFIELD_IDX_MAP.RT.clone() : JXRB_WARFIELD_IDX_MAP.LT.clone();
		// }
		// else if (this.actionRaw.targetType == JXETargetPosType.SelfCenter) {
		// 	targetPos = this.execer.dir == JXEDir.Right ? JXRB_WARFIELD_IDX_MAP.RC.clone() : JXRB_WARFIELD_IDX_MAP.LC.clone();
		// }
		// else if (this.actionRaw.targetType == JXETargetPosType.TargetCenter) {
		// 	targetPos = this.targets[0].dir == JXEDir.Right ? JXRB_WARFIELD_IDX_MAP.RC.clone() : JXRB_WARFIELD_IDX_MAP.LC.clone();
		// } else if (this.actionRaw.targetType == JXETargetPosType.TargetCntAft) {
		// 	var target = this.targets[0];
		// 	targetPos = target.position;
		// 	let index = target.dir == JXEDir.Right ? -1 : 1;
		// 	targetPos.x = JXRB_WARFIELD_IDX_MAP[index][2].clone().x;
		// }
		return targetPos;
	}

	protected createBullet(target: JXRBRole, curSegment: number, effects: JXAnsSkillEffect[]): JXRBBullet {
		let bulletIdx = curSegment;
		let bulletLen = this.actionRaw.bullet.length;
		if (bulletIdx >= bulletLen) {
			bulletIdx = bulletIdx % bulletLen;
		}
		let id = this.actionRaw.bullet[bulletIdx];
		let offset = this.actionRaw.bulletInterval ? this.actionRaw.bulletInterval : 0;
		let bulletConfig = GameMgr.bullet.getRaw<SBulletRaw>(id);
		let bullet = new JXRBBullet(bulletConfig, this, this._cmd)
			.bindHiter(target)
			.bindOrder(curSegment).bindDelayOffset(offset)
			.bindEndExc(effects)
			.generate();
		return bullet;
	}

	protected createBullets() {
		if (!this.isBullet(this.actionRaw.bullet)) return;
		this._bullets = this._bullets || [];
		this.tagBullet();
		if (this.targets.length) {
			let target = this.targets[JXBtlArrIx.Zero];
			let dir = (this.execer.x > target.x ? JXEDir.Left : JXEDir.Right);
			this.execer.setDir(dir)
		}
		if (!this._aniConfig.isSkill) {
			let bullet = this.createBullet(this.targets[JXBtlArrIx.Zero], 0, null);
			this._bullets.push(bullet);
			return;
		}

		let ansSkill = this._aniConfig.ansInfo as JXAnsSkillInfo;
		let curSegment = 0;
		if (this.collsionType == JXECollsionType.Effect) {
			for (let j = 0; j < ansSkill.effects.length; j++) {
				let ansEffect = ansSkill.effects[j];
				let target = this._cmd.getRoleById(ansEffect.target);
				let bullet = this.createBullet(target, curSegment, [ansEffect]);
				this._bullets.push(bullet);
				curSegment++;
			}
		}
		else {
			let exist = [];
			for (let i = 0; i < this.targets.length; i++) {
				let targetKey = this.targets[i].id;
				if (exist.indexOf(targetKey) != INVALID_VALUE) continue;
				exist.push(targetKey);
				let ansEffect = [];
				for (let j = 0; j < ansSkill.effects.length; j++) {
					if (ansSkill.effects[j].target == targetKey) {
						ansEffect.push(ansSkill.effects[j])
					}
				}
				let bullet = this.createBullet(this.targets[i], curSegment, ansEffect);
				this._bullets.push(bullet);
				curSegment++;
			}
		}
	}

	public shoots() {
		this.playAudio();
		if (this._bullets && this._bullets.length > 0) {
			this._bullets.forEach(bullet => bullet.shoot());
		}
		this.isShoots = true;
	}


	public getCurSegmentAniName(): number {
		let segmentIdx = this._curSegment;
		let aniLength = this.actionRaw.segmentIdxs.length;
		if (segmentIdx >= aniLength) {
			segmentIdx = segmentIdx % aniLength;
		}
		return this.actionRaw.segmentIdxs[segmentIdx];
	}

	/** 动画事件触发 */
	public onAnimationCollision(track: sp.spine.TrackEntry, eventName: string) {
		if (!this._aniConfig.ansInfo) return;

		let hurtMul = 0, cureMul = 0, isEffect = false;
		if (eventName == JXEAniEventName.Effects || eventName == "Effects") {
			isEffect = true;
			eventName = JXEAniEventName.Effects;
		}
		else {
			let info = eventName.split('_');
			let value = Number(info[1]) || 0;
			if (info[0] == JXEAniEventName.Hurt) {
				hurtMul = value;
				eventName = JXEAniEventName.Hurt;
			}
			else if (info[0] == JXEAniEventName.Cure) {
				cureMul = value;
				eventName = JXEAniEventName.Cure;
			}
		}

		// 普通攻击
		if (!this._aniConfig.isSkill) {
			let ansHurt = this._aniConfig.ansInfo as JXAnsHurtInfo;
			this.dealComAtk(ansHurt, this.targets[JXBtlArrIx.Zero], hurtMul);
			this.onCollisionEffect(ansHurt.protect ? JXECollisionEffectFlag.Damage1 : JXECollisionEffectFlag.Damage, this.targets[JXBtlArrIx.Zero]);
			return;
		}

		let ansSkill = this._aniConfig.ansInfo as JXAnsSkillInfo;
		let skillRes = JXBattleCb.getSkillData(ansSkill.skillId);

		if (this.collsionType == JXECollsionType.Effect) {
			let index = this._curSegment || 0;

			let sEffects = skillRes.skillEffect[index];
			if (!sEffects) {
				cc.error('the skill config is error, the action segment is not equal skill effects.length! ');
				return;
			}
			let effectId = sEffects[JXBtlArrIx.Zero];

			for (let j = 0; j < ansSkill.effects.length; j++) {
				if (ansSkill.effects[j].effectId == effectId) {
					let collisionFlag = this.dealSkillEffect(ansSkill.effects[j], {
						cureMul,
						isEffect,
						isBullet: false,
						hurtMul,
						eventName,
					});
					let target = this._cmd.getRoleById(ansSkill.effects[j].target);
					this.onCollisionEffect(collisionFlag, target);
				}
			}
		} else if (this.collsionType == JXECollsionType.InsEffect) {
			let index = this._curSegment || 0;
			let collisionFlag = this.dealSkillEffect(ansSkill.effects[index], {
				cureMul,
				isEffect,
				isBullet: false,
				hurtMul,
				eventName,
			});
			let target = this._cmd.getRoleById(ansSkill.effects[index].target);
			this.onCollisionEffect(collisionFlag, target);
		} else {
			let targetKeys = [];
			let collisionFlags = []
			for (let i = 0; i < ansSkill.effects.length; i++) {
				let ansEffect = ansSkill.effects[i];
				let targetKey = ansEffect.target;
				let index = targetKeys.indexOf(targetKey);
				if (index == INVALID_VALUE) {
					index = targetKeys.push(targetKey) - 1;
				}
				collisionFlags[index] = this.dealSkillEffect(ansEffect, {
					cureMul,
					isEffect,
					isBullet: false,
					hurtMul,
					eventName,
				});
			}
			for (let i = 0; i < collisionFlags.length; i++) {
				let target = this._cmd.getRoleById(targetKeys[i]);
				if (!target) continue;
				this.onCollisionEffect(collisionFlags[i], target)
			}
		}
	}


	/**触发爆点效果*/
	protected onCollisionEffect(effectFlag: any, target: JXRBRole, nSegment: number = 0) {
		let collisionIds = this.actionRaw.collision || [];
		if (collisionIds.length > 0) {
			let index = nSegment % collisionIds.length;
			let collisionRaw = GameMgr.actionCollision.getRaw<SActionCollisionRaw>(collisionIds[index]);
			let collision: JXRBCollisionEffect = null;
			if (collisionRaw) {
				collision = new JXRBCollisionEffect(collisionRaw, this._cmd);
				collision.addToTarget(target);
				return;
			}
		}

		// if (effectFlag == JXECollisionEffectFlag.Damage) {
		// 	target.createEffect(JXStaticEffects.hit);
		// }
		// else if (effectFlag == JXECollisionEffectFlag.Damage1) {
		// 	target.createEffect(JXStaticEffects.hit1);
		// }
		// else if (effectFlag == JXECollisionEffectFlag.AddHp) {
		// 	target.createEffect(JXStaticEffects.cure);
		// }
	}

	/** 伤害分段值获取 */
	protected getPercentHurtAns(ans: JXAnsHurtInfo, hurtMul?: number) {

		let ret = OBJECT_COPY<JXAnsHurtInfo>(ans);
		let nSegment = (ans && this._ansSegmentInfos) ? this._ansSegmentInfos[ans.insId] : null;
		if (!nSegment) {
			nSegment = 0;
		}
		let oldSegment = nSegment;
		let totalDamage = ans.damage + ans.protect;
		if (!hurtMul) {
			if (nSegment) {
				ret.damage = totalDamage - nSegment;
			}
			else {
				ret.damage = totalDamage;
			}
			nSegment = totalDamage;
		}
		else {
			ret.damage = Math.floor(totalDamage * hurtMul);
			nSegment += ret.damage;
		}

		// 处理扣护盾的逻辑
		let damage = ret.damage;
		if (oldSegment < ans.protect) {
			if ((damage + oldSegment) < ans.protect) {
				ret.protect = damage;
				ret.damage = 0;
			}
			else {
				let sub = ans.protect - oldSegment;
				ret.damage = damage - sub;
				ret.protect = sub;
			}
		}
		else {
			ret.protect = null;
		}

		// 如果不是最后一段，不走其他表现
		if (this._ansSegmentInfos) {
			this._ansSegmentInfos[ans.insId] = nSegment;
		}
		if (nSegment < totalDamage) {
			ret.ins2Hurt = null;
			ret.ins2AtkHp = null;
			ret.ins2DefHp = null;
			ret.targetIsDie = false;
		}

		return ret;
	}


	/** 治疗分段值获取 */
	protected getPercentCureAns(ans: JXAnsAddHpInfo, cureMul?: number) {
		let ret = OBJECT_COPY<JXAnsAddHpInfo>(ans);
		let nSegment = this._ansSegmentInfos[ans.insId];
		if (!nSegment) {
			nSegment = 0;
		}
		if (!cureMul) {
			if (nSegment) {
				ret.addNum = ans.addNum - nSegment;
			}
		}
		else {
			ret.addNum = Math.floor(ans.addNum * cureMul);
			nSegment += ret.addNum;
		}
		this._ansSegmentInfos[ans.insId] = nSegment;
		return ret;
	}

	/** 最大血量分段值获取 */
	protected getPercentMaxHpAns(ans: JXAnsAddMaxHpInfo, cureMul?: number) {
		let ret = OBJECT_COPY<JXAnsAddMaxHpInfo>(ans);
		let nSegment = this._ansSegmentInfos[ans.insId];
		if (!nSegment) {
			nSegment = 0;
		}
		if (!cureMul) {
			if (nSegment) {
				ret.addNum = ans.addNum - nSegment;
			}
		}
		else {
			ret.addNum = Math.floor(ans.addNum * cureMul);
			nSegment += ret.addNum;
		}
		this._ansSegmentInfos[ans.insId] = nSegment;
		return ret;
	}


	protected dealSkillEffect(effect: JXAnsSkillEffect, event: IEffectEvent) {
		let cmdMgr = this._cmd;
		let jxBtlAns = cmdMgr.jxBtlAns;
		let ansBuff = jxBtlAns.buffInfos[effect.buffInsId];
		let buffRaw = GameMgr.skillEffect.getRaw<SSkillEffectRaw>(ansBuff.buffId);
		if (!buffRaw) return;
		let ansSkill = jxBtlAns.skillInfos[ansBuff.insSkillId];

		if (ansBuff.prepareRd) {
			// buff准备处理
			(event.isEffect || event.isBullet) && cmdMgr.addBuff(effect.target, effect.buffInsId, ansSkill.skillId, ansBuff);
		}

		else {
			if (ansBuff.lifeRd) {
				// buff存在持续时间，buff添加处理
				(event.isEffect || event.isBullet) && cmdMgr.addBuff(effect.target, effect.buffInsId, ansSkill.skillId, ansBuff);
			}
			let target = cmdMgr.getRoleById(ansBuff.target);
			// buff立即生效
			switch (buffRaw.effectMold) {
				case JXBtlBEM.Hurt: {
					if (event.isEffect && !event.isBullet) break;
					let hurtDamage = jxBtlAns.hurtInfos[ansBuff.effects[JXBtlArrIx.Zero]];
					if (!hurtDamage) {
						// if need -> show Miss;
						target && target.showDescribe({ type: DescribeType.Hurt, text: "-0" });
						return;
					}
					let segmentAns = this.getPercentHurtAns(hurtDamage, event.hurtMul);
					target && target.onDamage(segmentAns);
					return segmentAns.protect ? JXECollisionEffectFlag.Damage1 : JXECollisionEffectFlag.Damage;
				}
				case JXBtlBEM.AddHp:
				case JXBtlBEM.AddProtect: {
					let addAns = jxBtlAns.addHpInfos[ansBuff.effects[JXBtlArrIx.Zero]];
					if (!addAns) {
						// if need -> show Miss;
						target && target.showDescribe({ type: DescribeType.Cure, text: "+0" });
						return;
					}
					let tempAns = this.getPercentCureAns(addAns, event.cureMul);
					target && target.onAddHp(tempAns);
					return JXECollisionEffectFlag.AddHp;
				}
				case JXBtlBEM.AddMaxHp: {

					let addMaxAns = jxBtlAns.addMaxHpInfos[ansBuff.effects[JXBtlArrIx.Zero]];
					if (!addMaxAns) {
						//target.showDescribe({type: DescribeType.MaxHp, text: target.cmd.numberParse("+0", false)});
						return;
					}
					let tempAns = this.getPercentMaxHpAns(addMaxAns, event.cureMul);
					target && target.onAddMaxHp(tempAns);
					return JXECollisionEffectFlag.AddHp;
				}
				case JXBtlBEM.Detonated: {
					if (!event.isEffect) return;
					if (ansBuff.effects.length == 0) {
						target.showDescribe({ type: DescribeType.Effect, text: buffRaw.effectDes + "_miss" });
						return;
					};
					let effects = ansBuff.effects;
					for (let i = 0; i < effects.length; i++) {
						let ybAnsBuff = jxBtlAns.buffInfos[effects[i].uuid];
						let ydRaw = GameMgr.skillEffect.getRaw<SSkillEffectRaw>(ybAnsBuff.buffId);
						if (!ydRaw) continue;
						let dyTarget = cmdMgr.getRoleById(ybAnsBuff.target);

						if (ydRaw.effectMold == JXBtlBEM.Hurt && effects[i].effect) {
							let ydDamage = jxBtlAns.hurtInfos[effects[i].effect];
							if (!ydDamage) {
								// if need -> show Miss;
								continue;
							}
							ydDamage.ext = buffRaw.effectDes;
							dyTarget.onDamage(ydDamage);
						}
						else if (ydRaw.effectMold == JXBtlBEM.AddHp && effects[i].effect) {
							let ydAddHp = jxBtlAns.addHpInfos[effects[i].effects];
							if (ydAddHp) continue;
							ydAddHp.ext = buffRaw.effectDes;
							dyTarget.onAddHp(ydAddHp);
						}
					}
					break;
				}
				default: {
					if (event.isEffect && !event.isBullet) return;
					cmdMgr.addBuff(effect.target, effect.buffInsId, ansSkill.skillId, ansBuff);
					break;
				}
			}

		}
		return JXECollisionEffectFlag.Config;
	}


	protected dealComAtk(ansHurt: JXAnsHurtInfo, target: JXRBRole, hurtMul?: number) {
		let segmentAns = this.getPercentHurtAns(ansHurt, hurtMul);
		target.onDamage(segmentAns);
	}

	/**
	  * 结束某个维度（动画，动作，子弹。。。）
	  * @param flag 结束来源
	  */
	protected onSessionEnd(flag: number) {
		this._endFlag &= ~flag;
		if (this._endFlag > 0) {
			return;
		}
		this.exit();
	}

	public isAllSessionEnd() {
		if (this._endFlag > 0) {
			return false;
		}
		return true;
	}


	/** 上动画标志 */
	protected tagAnimation() { this._endFlag |= END_FLAG.ANIMATION };
	/** 上动作标志 */
	protected tagAction() { this._endFlag |= END_FLAG.ACTION };
	/** 上子弹标志 */
	protected tagBullet() { this._endFlag |= END_FLAG.BULLET };
	/** 子弹完成 */
	protected bulletEnd() { this.onSessionEnd(END_FLAG.BULLET); }
	/** 动作完成 */
	protected actionEnd() {
		this.onSessionEnd(END_FLAG.ACTION);
	}
	/** 动画完成 */
	protected animationEnd() {
		this.onSessionEnd(END_FLAG.ANIMATION);
	}

	/** 子弹结束（命中） */
	public onBulletCollision(bullet: JXRBBullet) {
		let index = this._bullets.indexOf(bullet);
		if (index == INVALID_VALUE) return;
		this._bullets.splice(index, 1);
		if (!this._aniConfig.isSkill) {
			this.onAnimationCollision(null, JXEAniEventName.Effects);
		}
		else {
			if (bullet.endExec) {
				let collisionFlag = null;
				for (let i = 0; i < bullet.endExec.length; i++) {
					let flag = this.dealSkillEffect(bullet.endExec[i], {
						cureMul: 0,
						isEffect: true,
						isBullet: true,
						hurtMul: 0,
						eventName: null,
					});
					if (!collisionFlag) {
						collisionFlag = flag;
					}
				}
				this.onCollisionEffect(collisionFlag, bullet.hiter, bullet.order);
			}
		}
		if (!this._bullets || this._bullets.length == 0) {
			this.bulletEnd();
		}
	}

	public isBullet(bullet) {
		return bullet && bullet.length > 0;
	}

}

/** 单次普通近战攻击 */
export class JXRBScript$OnceNearAtk extends JXRBScript {
	public get ID(): JXEScriptID {
		return JXEScriptID.OnceNearAtk;
	}

	public get collsionType(): JXECollsionType {
		return JXECollsionType.Siglon;
	}

	public exec() {
		super.exec();
		let target = this.targets[0];
		let airPos = this.getSingleTargetPos();
		let aniName = this.getBaseAnimName();
		this.tagAnimation();
		this.tagAction();
		let animation: cc.Animation = null
		let dir = (airPos.x > target.x ? JXEDir.Left : JXEDir.Right);
		this.execer.setDir(dir);
		if (this.actionRaw.effect) {
			let anim = this.execer.changeAnimation(aniName, false, () => {
				animation = this._cmd.assetManager.createEffect(this.actionRaw.effect as any, JXBattleEffectsRoot, cc.WrapMode.Normal);
				animation.node.scaleX = this.execer.dir === JXEDir.Right ? this.actionRaw.effect[JXBtlArrIx.Five] : -this.actionRaw.effect[JXBtlArrIx.Five];
				this.playAudio();
				animation.once(cc.Animation.EventType.FINISHED, () => {
					this.execer.changeAnimation(JXEAniNames.back, false, this.animationEnd.bind(this))
					animation.node.destroy();
					animation = null;
				}, this);
				animation.node.position = cc.v2(this.actionRaw.position[0], this.actionRaw.position[1])
				let layer = this.execer.target.getChildByName(JXRB_ROLE_LAYER_NAME.UPPER);
				animation.node.parent = layer;
				animation.play(COMMON_ANI_NAME);
				let duration = (animation ? animation.getClips()[0].duration : 0)
				this.execer.addAction(cc.delayTime(duration));
				this.execer.addAction(cc.moveTo(MOVE_TIME, this.execer.birthPos).endCallback(function () {
					if (!this._aniConfig.isSkill) {
						let ansHurt = this._aniConfig.ansInfo as JXAnsHurtInfo;
						this.dealComAtk(ansHurt, this.targets[JXBtlArrIx.Zero])
					}
					this.actionEnd()
				}.bind(this)));
			});
			if (this.actionRaw.airOffsetX) {
				airPos.x += this.actionRaw.airOffsetX * target.dir;
			} else {
				airPos.x += (target.skeleton.node["sWidth"] / 2 + this.execer.skeleton.node["sWidth"] / 2) * target.dir;
			}
			this.execer.addAction(cc.moveTo(MOVE_TIME, airPos));
		} else {
			this.playAudio();
			let anim = this.execer.changeAnimation(aniName, false, this.animationEnd.bind(this));
			if (this.actionRaw.airOffsetX) {
				airPos.x += this.actionRaw.airOffsetX * target.dir;
			} else {
				airPos.x += (target.skeleton.node["sWidth"] / 2 + this.execer.skeleton.node["sWidth"] / 2) * target.dir;
			}
			this.execer.addAction(cc.moveTo(MOVE_TIME, airPos));
			this.execer.addAction(cc.delayTime(anim.duration));
			this.execer.addAction(cc.moveTo(MOVE_TIME, this.execer.birthPos).endCallback(function () {
				if (!this._aniConfig.isSkill) {
					let ansHurt = this._aniConfig.ansInfo as JXAnsHurtInfo;
					this.dealComAtk(ansHurt, this.targets[JXBtlArrIx.Zero])
				}
				this.actionEnd()
			}.bind(this)));
		}
	}
}


/**根据路径移动动作 */
export class JXRBScript$RoadAct extends JXRBScript {
	public curPosIdx: number = 0;
	public road: cc.Vec2[] = null;

	public get ID(): JXEScriptID {
		return JXEScriptID.RoadMoveAct;
	}

	public get collsionType(): JXECollsionType {
		return JXECollsionType.Siglon;
	}

	public exec() {
		super.exec();
		this.curPosIdx = 0;
		let pos = this.execer.cmd.btlMapConverts.converToMapPos(this.execer.birthPos);
		let res = JXBattleCb.getCardData(this.execer.tableId);
		let adj = GameMgr.mapMgr.getAdjacent(res.scope, pos);
		this.road = adj;
		this.playAudio();
		this.nextAction();
	}

	public nextAction() {
		if (this.curPosIdx == this.road.length) {
			this.execer.addAction(cc.moveTo(MOVE_TIME, this.execer.birthPos).endCallback(this.exit.bind(this)));
			return;
		}

		let airPos = this.execer.cmd.btlMapConverts.converToCCPos(this.road[this.curPosIdx]);
		let aniName = this.getBaseAnimName(airPos);
		let anim = this.execer.changeAnimation(aniName);
		this.execer.addAction(cc.moveTo(MOVE_TIME * 2, airPos).endCallback(this.nextAction.bind(this)));
		this.curPosIdx++;
	}
}


/** 多目标普通攻击 */
export class JXRBScript$MulNearAtk extends JXRBScript {

	public curTargetIdx: number = 0;

	public get ID(): JXEScriptID {
		return JXEScriptID.MulNearAtk;
	}

	public get collsionType(): JXECollsionType {
		return JXECollsionType.Siglon;
	}

	public exec() {
		super.exec();
		this.curTargetIdx = 0;
		this.nextAction();
	}

	public nextAction() {
		if (this.curTargetIdx == this.targets.length) {
			this.execer.addAction(cc.moveTo(MOVE_TIME, this.execer.birthPos).endCallback(this.exit.bind(this)));
			return;
		}
		let aniName = this.getBaseAnimName();
		this.playAudio();
		let anim = this.execer.changeAnimation(aniName);
		let target = this.targets[this.curTargetIdx];
		let airPos = target.position
		if (this.actionRaw.airOffsetX) {
			airPos.x += this.actionRaw.airOffsetX * target.dir;
		} else {
			airPos.x += (target.skeleton.node["sWidth"] / 2 + this.execer.skeleton.node["sWidth"] / 2) * target.dir;
		}
		this.execer.addAction(cc.moveTo(MOVE_TIME, airPos));
		this.execer.addAction(cc.delayTime(anim.duration - MOVE_TIME).endCallback(this.nextAction.bind(this)))
		this.curTargetIdx++;
	}
}
/**原地释放子弹 远程子弹*/
export class JXRBScript$BulletAtk extends JXRBScript {
	public endIndex: number;

	public get ID(): JXEScriptID {
		return JXEScriptID.BulletAtk;
	}

	public get collsionType(): JXECollsionType {
		if (this.actionRaw.bulletType) {
			return JXECollsionType.Siglon;
		}
		else {
			return JXECollsionType.Effect;
		}
	}

	public exec() {
		super.exec();
		this.tagAnimation();
		let anims = this.getComFullAnimNames();
		this.execer.changeSkillAnimation(anims, this.animationEnd.bind(this));
		this.createBullets();
	}
}
/**漂移到目的地-释放子弹 */
export class JXRBScript$DriftActBullet extends JXRBScript$BulletAtk {
	public get ID(): JXEScriptID {
		return JXEScriptID.DriftActBullet;
	}

	public exec() {
		JXRBScript$DriftAct.prototype.exec.call(this);
		this.createBullets();
	}
}

/**原地开始，目的地结束-释放子弹  */
export class JXRBScript$SyncMoveActBullet extends JXRBScript$BulletAtk {
	public get ID(): JXEScriptID {
		return JXEScriptID.SyncMoveActBullet;
	}

	public exec() {
		JXRBScript$SyncMoveAct.prototype.exec.call(this);
		this.createBullets();
	}
}

/**原地释放 */
export class JXRBScript$StandAct extends JXRBScript {
	public get ID(): JXEScriptID {
		return JXEScriptID.StandAct;
	}

	public get collsionType(): JXECollsionType {
		return JXECollsionType.Siglon;
	}

	public exec() {
		super.exec();
		let anims = this.getComFullAnimNames();
		this.tagAnimation();
		let self = this;
		if (!self._aniConfig.isSkill) {
			let ansHurt = self._aniConfig.ansInfo as JXAnsHurtInfo;
			self.targets[JXBtlArrIx.Zero].onDamage(ansHurt);
		}
		this.execer.changeSkillAnimation(anims, () => {
			this.animationEnd();
		});
		this.playAudio();
		this.createBullets();
	}
}

/**漂移目的地释放 */
export class JXRBScript$DriftAct extends JXRBScript {
	public get ID(): JXEScriptID {
		return JXEScriptID.DriftAct;
	}

	public get collsionType(): JXECollsionType {
		return JXECollsionType.Siglon;
	}

	public exec() {
		super.exec();
		let targetPos = this.getSingleTargetPos();
		if (targetPos != this.execer.position) {
			let dir = (targetPos.x > this.targets[0].x ? JXEDir.Left : JXEDir.Right);
			this.execer.setDir(dir);
		}
		let anims = this.getComFullAnimNames();
		this.tagAnimation();
		this.playAudio();
		let moveTo = cc.moveTo(this.actionRaw.moveTime, targetPos).endCallback(() => this.execer.changeSkillAnimation(anims, this.animationEnd.bind(this)));
		this.execer.addAction(moveTo);
	}
}

/**原地开始， 目的地结束 */
export class JXRBScript$SyncMoveAct extends JXRBScript {
	public get ID(): JXEScriptID {
		return JXEScriptID.SyncMoveAct;
	}

	public get collsionType(): JXECollsionType {
		return JXECollsionType.Siglon;
	}

	public exec() {
		super.exec();
		let targetPos = this.getSingleTargetPos();
		if (targetPos != this.execer.position) {
			let dir = (targetPos.x > this.targets[0].x ? JXEDir.Left : JXEDir.Right);
			this.execer.setDir(dir);
		}
		let anims = this.getComFullAnimNames();
		this.tagAction();
		this.tagAnimation();
		this.playAudio();
		let aniInfo = this.execer.changeSkillAnimation(anims, this.animationEnd.bind(this));
		this.execer.addAction(cc.moveTo(aniInfo.duration, targetPos).endCallback(this.actionEnd.bind(this)));
	}
}

/**分段动作 */
export class JXRBScript$SegmentAct extends JXRBScript {
	public get ID(): JXEScriptID {
		return JXEScriptID.SegmentAct;
	}

	public get collsionType(): JXECollsionType {
		return JXECollsionType.Siglon;
	}

	public exec() {
		this._curSegment = null;
		this.tagAnimation();
		this.nextSegment();
	}

	protected nextSegment() {
		if (this._curSegment === null) {
			this._segmentCount = GameMgr.action.getRaw<SActionRaw>(this._aniConfig.actionId).segmentIdxs.length;
			this._curSegment = 0;
		}
		else {
			this._curSegment++;
		}
		if (this._curSegment == this._segmentCount) {
			this.animationEnd();
			return;
		}
		if (this._curSegment == 0) {
			let targetPos = this.getSingleTargetPos();
			let anims = this.getComFullAnimNames();
			let aniBase = this.getBaseAnimName();
			this.playAudio();
			anims[anims.length - 1] = this.appendAnimName(aniBase, this.getCurSegmentAniName());
			let action = this.execer.changeSkillAnimation(anims, this.nextSegment.bind(this));
			let moveTo = cc.moveTo(action.duration, targetPos);
			this.execer.addAction(moveTo);
		}
		else {
			let aniBase = this.getBaseAnimName();
			this.execer.changeAnimation(this.appendAnimName(aniBase, this.getCurSegmentAniName()), false, () => {
				this.nextSegment();
			});
		}
	}
}

/**闪现动作 */
export class JXRBScript$SegmentTAct extends JXRBScript {
	public get ID(): JXEScriptID {
		return JXEScriptID.SegmentAct;
	}

	public get collsionType(): JXECollsionType {
		return JXECollsionType.Siglon;
	}

	public exec() {
		this._curSegment = null;
		this.tagAnimation();
		this.nextSegment();
	}

	protected nextSegment() {
		if (this._curSegment === null) {
			this._segmentCount = GameMgr.action.getRaw<SActionRaw>(this._aniConfig.actionId).segmentIdxs.length;
			this._curSegment = 0;
		}
		else {
			this._curSegment++;
		}
		if (this._curSegment == this._segmentCount) {
			this.animationEnd();
			return;
		}
		let anims = this.getComFullAnimNames();
		let aniBase = this.getBaseAnimName();
		anims[anims.length - 1] = this.appendAnimName(aniBase, this.getCurSegmentAniName());
		if (this._curSegment == 0) {
			let targetPos = this.getSingleTargetPos();
			let action = this.execer.changeAnimation(anims[anims.length - 1], false, () => {
				let move = cc.sequence(cc.fadeOut(0.08), cc.moveTo(action.duration, targetPos), cc.callFunc(this.nextSegment.bind(this)))
				this.playAudio();
				this.execer.addAction(move)
			});
		}
		else {
			if (this.execer.target.opacity === 0) {
				let action = cc.spawn(cc.fadeIn(0.08), cc.callFunc(() => {
					this.execer.changeAnimation(anims[anims.length - 1], false, () => {
						this.nextSegment();
					});
				}))
				this.execer.addAction(action);
			} else {
				this.execer.changeAnimation(anims[anims.length - 1], false, () => {
					this.nextSegment();
				});
			}
		}
	}
}

/**不显示人物的分段动画 */
export class JXRBScript$SegmentNAct extends JXRBScript {
	public get ID(): JXEScriptID {
		return JXEScriptID.SegmentAct;
	}

	public get collsionType(): JXECollsionType {
		return JXECollsionType.InsEffect;
	}

	public exec() {
		this._curSegment = null;
		this.tagAnimation();
		this.nextSegment();
	}

	protected nextSegment() {
		if (this._curSegment === null) {
			this._curSegment = 0;
		}
		else {
			this._curSegment++;
		}
		if (this._curSegment == this._segmentCount) {
			this.animationEnd();
			return;
		}
		let anims = this.getComFullAnimNames();
		let aniBase = this.getBaseAnimName();
		anims[anims.length - 1] = this.appendAnimName(aniBase, this.getCurSegmentAniName());
		if (this._curSegment == 0) {
			let targetPos = this.getSingleTargetPos();
			let action = this.execer.changeAnimation(anims[anims.length - 1], false, () => {
				let move = cc.sequence(cc.fadeOut(0.08), cc.moveTo(action.duration, targetPos), cc.callFunc(this.nextSegment.bind(this)))
				this.execer.addAction(move)
				this.playAudio();
			});
		}
		else {
			let sk = this.execer.target.getChildByName("skeleton")
			let lower = this.execer.target.getChildByName(JXRB_ROLE_LAYER_NAME.LOWER)
			let up = this.execer.target.getChildByName(JXRB_ROLE_LAYER_NAME.UPPER)
			if (this.execer.target.opacity === 0) {
				let action = cc.spawn(cc.fadeIn(0.08), cc.callFunc(() => {
					cc.tween(sk).to(0.6, { opacity: 0 }).start()
					cc.tween(lower).to(0.6, { opacity: 0 }).start()
					cc.tween(up).to(0.6, { opacity: 0 }).start()
					this.execer.changeAnimation(anims[anims.length - 1], false, () => {
						cc.tween(sk).to(0.2, { opacity: 255 }).start()
						cc.tween(lower).to(0.6, { opacity: 255 }).start()
						cc.tween(up).to(0.6, { opacity: 255 }).start()
						this.nextSegment();
					});
				}))
				this.execer.addAction(action);
			} else {
				cc.tween(sk).to(0.6, { opacity: 0 }).start()
				this.execer.changeAnimation(anims[anims.length - 1], false, () => {
					cc.tween(sk).to(0.2, { opacity: 255 }).start()
					this.nextSegment();
				});
			}
		}
	}

}


/**光环 */
export class JXRBScript$Ambient extends JXRBScript$StandAct {
	public get ID(): JXEScriptID {
		return JXEScriptID.Ambient;
	}

	public get collsionType(): JXECollsionType {
		return JXECollsionType.Siglon;
	}

	public exec() {
		let aniName = this.appendAnimName(this.getBaseAnimName(), JXBtlArrIx.One);
		this.tagAnimation();
		this.execer.changeSkillAnimation([aniName], this.animationEnd.bind(this));
		this.createBullets();
	}
}